package com.huntmobi.springsecuritydemo.base.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.huntmobi.springsecuritydemo.base.enums.APIEnum;
import com.huntmobi.springsecuritydemo.base.enums.ContextTypeEnum;
import com.huntmobi.springsecuritydemo.base.enums.TokenEnum;
import com.huntmobi.springsecuritydemo.base.http.Response;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

/**
 * <p>
 *
 * @Author：zouzhimin
 * @description：当其他请求发送来，校验token的过滤器，如果校验成功，就让请求继续执行
 * @date：crealed in 15:14 2020/7/3
 * </P>
 **/
@Component
public class JwtFilter extends OncePerRequestFilter {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Override
    public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        response.setContentType(ContextTypeEnum.JSONUTF8.getContextType());
        // 1.如果是登录接口，就直接过
        if (request.getRequestURI().contains(APIEnum.LOGIN.getApi())) {
            filterChain.doFilter(request, response);
            return;
        }
        // 2.获取请求头中的token
        String token = request.getHeader(TokenEnum.TOKENHEADER.getMsg());
        // 3..判断token是否为空
        if (StringUtils.isEmpty(token)) {
            response.getWriter().write(new ObjectMapper().writeValueAsString(Response.error(TokenEnum.TOKENEMPTY.getCode(), TokenEnum.TOKENEMPTY.getMsg())));
            return;
        }
        // 4.判断token是否在redis中，没有，则登录
        if (ObjectUtils.isEmpty(redisTemplate.opsForValue().get(token.replaceAll(TokenEnum.TOKENPREFIX.getMsg(), "").trim()))) {
            response.getWriter().write(new ObjectMapper().writeValueAsString(Response.error(TokenEnum.TOKENILLEGAL.getCode(), TokenEnum.TOKENILLEGAL.getMsg())));
            return;
        }

        // 5.解析token,并且使用token作为登录凭证
        tokenAuthentication(request, response, token);

        filterChain.doFilter(request, response);
    }

    /**
     * 设置token凭证
     */
    private void tokenAuthentication(HttpServletRequest request, HttpServletResponse response, String token) {
        try {
            // 判断前端传过来的token格式是否正确
            if (token.startsWith(TokenEnum.TOKENPREFIX.getMsg())) {

                Claims claims = Jwts.parser().setSigningKey("sang@123").parseClaimsJws(token.replace(TokenEnum.TOKENPREFIX.getMsg(), "")).getBody();
                //获取当前登录用户信息
                String sysLoginSuccess = claims.getSubject();
                //判断用户是否得到了认证
                if (!isAuthentication(sysLoginSuccess)) {
                    List<GrantedAuthority> authorities = AuthorityUtils.commaSeparatedStringToAuthorityList((String) claims.get(TokenEnum.TOKENHEADER.getMsg()));
                    UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(sysLoginSuccess, null, authorities);
                    usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                    SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
                }
            }
        } catch (Exception e) {
            try {
                response.getWriter().write(new ObjectMapper().writeValueAsString(Response.error(TokenEnum.TOKENFORMAT.getCode(), TokenEnum.TOKENFORMAT.getMsg())));
            } catch (IOException ioException) {
                ioException.printStackTrace();
            }
        }
    }

    /**
     * 判断用户是否得到了认证
     */
    private boolean isAuthentication(String userName) {
        if (StringUtils.isEmpty(userName) && ObjectUtils.isEmpty(SecurityContextHolder.getContext().getAuthentication())) {
            return true;
        }
        return false;
    }

}
